/**
 * @file    meta_prog.h
 * @author  Ying-Po Liao <lrobin@neo.tamu.edu>
 * @version 1.0
 *
 * @section This file includes all stuff used for
 *	    meta-programming
 *
 * CSCE-689 Design Using C++
 * Final Project
 */

#ifndef META_PROG_H
#define META_PROG_H

template <class task_type, int n>
struct unroll
{
	static void iteration( int i, task_type task )
	{
		task(i);
		unroll<task_type, n-1>::iteration( i+1, task );
	}
};

template <class task_type>
struct unroll<task_type, 0>
{
	static void iteration( int i, task_type task ) {}
};

/**
 *  @brief  main interface for loop-unrolling
 *  @tparam unroll_size. The number of loops will be expanded (unrolled) at compile-time
 *
 *  loop-unrolling is going to expand for-loop at compile-time using recursive template.
 *  For example, if total_size = 100, unroll_size = 4, then only 25 for-loops will be
 *  execute. Each for-loop will run 4 exapnded statements by calling task(i), where task
 *  is any callable callback instance and i is the current looping number.
 */
template <int unroll_size = 4>
struct unroll_loop
{
	/**
	 *  @brief  main interface for loop-unrolling
	 *  @tparam task_type. Any callable type for callback while looping
	 *  @param  total_size. Total loop size.
	 *  @param  task. Callable instance type of tast_type
	 */
	template <class task_type>
	static void loop( int total_size, task_type task ) 
	{
		for( int i = 0; i < total_size / unroll_size; i++ )
			unroll<task_type, unroll_size>::iteration( i * unroll_size, task );
		for( int i = 0; i < total_size % unroll_size; i++ )
			task( unroll_size * (total_size / unroll_size) + i );
	}
};

template <unsigned int N, unsigned cur_N>
struct dot_meta
{
	template <typename vec_type>
	inline static typename vec_type::num_type
	get( const vec_type& a, const vec_type& b )
	{
		return a[cur_N] * b[cur_N] +
			dot_meta<N, cur_N+1>::get( a, b );
	}
};

template <unsigned int N>
struct dot_meta<N, N>
{
	template <typename vec_type>
	inline static typename vec_type::num_type
	get( const vec_type& a, const vec_type& b )
	{ return typename vec_type::num_type(0); }
};

#endif //!< META_PROG_H